﻿/*
 * Copyright (C) 2012, Jesus Requejo <jesus.requejo@gmail.com> 
 * Licensed under Microsoft Public License (Ms-PL)
 * http://www.microsoft.com/en-us/openness/licenses.aspx#MPL
*/

namespace Infrastructure {

   using System;
   using System.Collections.Generic;
   using System.Linq;
   using System.Text;
   using System.IO;
   using System.Threading;

   public class FileUtils {

      private static readonly int m_attempts = 3;
      private static readonly int m_delayBetweenAttempts = 30;

      public static FileStream TryOpen(string filePath) {
         return TryOpen(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, m_attempts, m_delayBetweenAttempts);
      }

      public static FileStream TryOpen(string filePath, FileMode fileMode, FileAccess fileAccess, FileShare fileShare) {
         return TryOpen(filePath, fileMode, fileAccess, fileShare, m_attempts, m_delayBetweenAttempts);
      }

      public static FileStream TryOpen(string filePath, FileMode fileMode, FileAccess fileAccess, FileShare fileShare, int maximumAttempts, int attemptDelayInMs) {
         FileStream fs = null;
         int attempts = 0;

         // Loop allow multiple attempts
         while (true) {
            try {
               fs = File.Open(filePath, fileMode, fileAccess, fileShare);

               //If we get here, the File.Open succeeded, so break out of the loop and return the FileStream
               break;
            }
            catch (Exception) {
               // IOException is thrown if the file is in use by another process.

               // Check the number of attempts to ensure no infinite loop
               attempts++;
               if (attempts > maximumAttempts) {
                  // Too many attempts,cannot Open File, break and return null 
                  fs = null;
                  break;
               }
               else {
                  // Sleep before making another attempt
                  Thread.Sleep(attemptDelayInMs);
               }
            }
         }

         return fs;
      }

      public static bool AppearsToBeValidExecutable(string path) {
         if (string.IsNullOrWhiteSpace(path)) {
            return false;
         }

         // path must include an extension
         var extension = Path.GetExtension(path); // including a period (.)
         if (string.IsNullOrWhiteSpace(extension)) {
            return false;
         }
         // extension must be valid
         var validExtensions = GetValidExtensionsList();
         bool extensionIsValid = validExtensions.Contains(extension.ToLowerInvariant());
         if (!extensionIsValid) {
            return false;
         }
         // base path must contain only allowed characters
         var directory = Path.GetDirectoryName(path);
         if (string.IsNullOrWhiteSpace(directory) || path.IndexOfAny(Path.GetInvalidPathChars()) != -1) {
            return false;
         }
         // fileName must contain only allowed characters
         var fileName = Path.GetFileNameWithoutExtension(path);
         if (string.IsNullOrWhiteSpace(fileName) || fileName.IndexOfAny(Path.GetInvalidFileNameChars()) != -1) {
            return false;
         }
         return true;
      }

      public static string GetFormattedValidExtensionFilter() {
         var filters = GetValidExtensionFilter();
         return filters.Aggregate((i, j) => i + "|" + j);
      }

      public static IList<string> GetValidExtensionsList() {
         var filter = GetValidExtensionFilter();
         return filter.Select(s => s.Split('|')[1].Substring(1)).ToList();
      }

      private static IList<string> GetValidExtensionFilter() {
         return new List<string>() {
             @"Executable files|*.exe",
             @"Batch files|*.bat",
             @"Application Shortcuts|*.lnk",
             @"management Console Snap-in|*.msc"
         };
      }
   }
}
